
# 初始化
from flask import Flask,flash
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
import pymysql
from datetime import timedelta
pymysql.install_as_MySQLdb()


app = Flask(__name__) #创建FLASK应用
app.secret_key = 'aasdfbrfcerf'

# 设置静态文件缓存过期时间  Ctrl+F5，清除浏览器缓存
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=1)
# 也可以这样写：
# app.send_file_max_age_default = timedelta(seconds=1)


#配置参数
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:xxx@127.0.0.1:3306/school'

# 设置sqlalchemy自动更跟踪数据库
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
# 禁止自动提交数据处理
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = False
#实例化
db = SQLAlchemy(app)
login_manager = LoginManager()  #实例化登录管理对象
login_manager.init_app(app)     # 初始化应用
login_manager.login_view = 'login' #设置用户登录识图函数 endpoint

#用户信息
from werkzeug.security import generate_password_hash,check_password_hash
# 使用 sha256 加密算法将字符串变为密文
import uuid
#uuid 模板的 uuid4 方法生成一个全球唯一码


class Role(db.Model):
    #定义表名
    __tablename__ = 'roles'

    #定义字段
    # db.Column表示是一个字段
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(16),unique=True)


    # 在一的一方，写关联
    # susers = db.relationship('Suser'):表示和Suser模型发送关联，增加了一个susers属性
    # backred = 'role':表示role是Suser要用的属性
    susers = db.relationship('Suser',backref = 'role')

    #repr()方法显示一个可读字符串
    def __repr__(self):
        return "<Role_id:{0}>".format(self.id)

from flask_login import UserMixin  # 引入用户基类

class Suser(UserMixin,db.Model):
    #定义表名
    __tablename__ = 'susers'
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(16),unique=True)
    _password = db.Column(db.String(255))
    Snum = db.Column(db.String(11),unique=True)
    phone = db.Column(db.String(11))

    # ForeignKey外键
    role_id = db.Column(db.Integer,db.ForeignKey('roles.id'))

    # def __repr__(self):
    #     return "<User_id:{0}>".format(self.id)
    # < User_id: 3 >

    @property
    def password(self):  # 定义一个外部使用的密码
        # raise AttributeError("密码不允许读取")
        return self._password

    @password.setter  # 设置密码加密
    def password(self, row_password):
        self._password = generate_password_hash(row_password)

    def check_password(self, row_password):  # 定义一个反向解密的函数
        result = check_password_hash(self._password, row_password)
        return result

@login_manager.user_loader
###加载用户的回调函数接收以Unicode字符串形式表示的用户标示符
###如果能找到用户，这个函数必须返回用户对象，否则返回None。
def load_user(user_id):
    return Suser.query.get(int(user_id))

# # 删除表
# db.drop_all()
# # 创建表
# db.create_all()
#
# # 创建测试数据
# ro1 = Role(name='admin')
# db.session.add(ro1)
# db.session.commit()
# # 再次插入一条数据
# ro2 = Role(name='user')
# db.session.add(ro2)
# db.session.commit()
#
# # 多条用户数据
# us1 = Suser(name='wang', password="123456", Snum='19034480401',phone='13215246221',role_id=ro1.id)
# us2 = Suser(name='zhan', password="201512", Snum='19034480402',phone='13215246221',role_id=ro2.id)
# us3 = Suser(name='chen', password='987654', Snum='19034480403',phone='13215246221',role_id=ro2.id)
# us4 = Suser(name='zhou', password='456789', Snum='19034480404',phone='13215246221',role_id=ro2.id)
# db.session.add_all([us1, us2, us3, us4])
# db.session.commit()


#登录页面
from wtforms import StringField,PasswordField,BooleanField,SubmitField,IntegerField
from wtforms.validators import DataRequired,EqualTo,Length,Regexp,Email
from flask_wtf import FlaskForm
from  wtforms import ValidationError

class LoginForm(FlaskForm):
    # 登录表单类
    # DataRequired检查输入字段是否为空
    username = StringField('用户名',validators=[DataRequired()])
    password = PasswordField('密码',validators=[DataRequired()])
    ###PasswordField类表示属性为type="password"的<input>元素。
    # remember_me = BooleanField('记住密码')
    ###BooleanField类表示复选框。
    submit1 = SubmitField('登录')


class RegistrationForm(FlaskForm):
    phone = StringField('请输入手机号',validators=[Length(11),Regexp('^([0-9]{1,})$')])
    stuNum = StringField('请输入学号',validators=[Length(11),Regexp('^([0-9]{1,})$')])
    name = StringField('请输入用户名', validators=[DataRequired(), Length(1, 64), Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,'Usernames must have only letters, ''numbers, dots or underscores')])
    ###WTForms提供的Regexp验证函数，确保username字段只包含字母，数字，下划线和点号。这个验证函数中的正则表达式后面的两个参数分别是正则表达式的旗标和验证失败时显示的错误消息。
    password1 = PasswordField('密码', validators=[DataRequired(), EqualTo('password2', message='Passwords must match.')])
        ###EqualTo验证函数可以验证两个密码字段中的值是否一致，他附属在两个密码字段上，另一个字段作为参数传入。
    password2 = PasswordField('再次确认密码', validators=[DataRequired()])
    submit2 = SubmitField('注册')

    def validate_username(self, field):
        if Suser.query.filter_by(name=field.data).first():
            raise ValidationError('Username already in use.')
    ###表单类中定义了以validate_开头且后面跟着字段名的方法，这种方法就和常规的验证函数一起调用


from flask import render_template,redirect,url_for,request,session
from flask_login import login_user

@app.route('/login/',methods=['GET','POST'])
def login():
    form = LoginForm()
    emsg = None
    # form.validate_on_submit() 可以判断用户是否完整的提交了表单，只对 POST 有效，所以可以用来判断请求方式
    if form.validate_on_submit():
        user_name = form.username.data
        password = form.password.data
        username = Suser.query.filter_by(name = user_name).first() # 从用户数据中查找用户记录
        # < User_id: 1 >
        if username:
            if username.check_password(password):  # 校验密码
                login_user(username)#,form.remember_me.data)  # 创建用户 Session
                # login_user()函数的原型：flask.ext.login.login_user(user, remember=False, force=False, fresh=True)
                session['user_id'] = user_name
                return redirect(request.args.get('next') or url_for('index'))
            else:
                emsg = "用户名或密码密码有误"
        else:
            emsg = '用户名或密码错误'
    return render_template('login.html',form=form,emsg=emsg)

import os
from flask_login import current_user,login_required
#验证页面
@app.route('/',methods=['GET','POST']) #首页
def index():
    user_id,Snum,phone = None,None,None
    if 'user_id' in session:
        user_id = session['user_id']
        Snum = Suser.query.filter_by(name = user_id).first().Snum
        phone = Suser.query.filter_by(name = user_id).first().phone

    return render_template('index.html',user_id = user_id,Snum = Snum,phone=phone)



@app.route('/code/<codeText>',methods=['GET','POST']) #在线编译
@login_required     #需要登录才能访问
def code(codeText):
    user_id,Snum,phone = None,None,None
    if 'user_id' in session:
        user_id = session['user_id']
        Snum = Suser.query.filter_by(name = user_id).first().Snum
        phone = Suser.query.filter_by(name = user_id).first().phone
    if request.method == 'POST':
        result = request.form['code']
        with open(r'D:\result.py', 'w', encoding='gbk') as file:
            files = file.write(result)
        command = r'python D:\result.py'  # 可以直接在命令行中执行的命令
        info = os.popen(command).read()  # 读取命令行的输出到一个list
        return  render_template('code.html',result=result,info=info,user_id = user_id,codeText=codeText,Snum = Snum,phone=phone)
    return render_template('code.html', username=current_user.name,result='',info='',user_id = user_id,codeText=codeText,Snum = Snum,phone=phone)
# current_user 是当前登录者，是 User 的实例，是 Flask-Login 提供全局变量i（ 类似于全局变量 g ）
# username 是模板中的变量，可以将当前登录者的用户名传入 index.html 模板

@app.route('/test/<num>')
@login_required     #需要登录才能访问
def test_num(num):
    user_id,Snum,phone = None,None,None
    if 'user_id' in session:
        user_id = session['user_id']
        Snum = Suser.query.filter_by(name = user_id).first().Snum
        phone = Suser.query.filter_by(name = user_id).first().phone
    filehtml = num+'.html'
    return render_template(filehtml,user_id = user_id,Snum = Snum,phone=phone)

from flask_login import logout_user
@app.route('/logout')    #登出
@login_required
def logout():
    if 'user_id' in session:
        session.pop('user_id', None)
    logout_user()
    return redirect(url_for('index'))

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        user = Suser(name=form.name.data,password=form.password1.data,Snum= form.stuNum.data,phone= form.phone.data,role_id=2)
        db.session.add(user)
        db.session.commit()
        flash('注册成功，你现在可以登录了.')
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

if __name__ == '__main__':
    app.run(debug = True)